grammar Fun;

@header {
package grammar;
import java.util.*;

}
@parser::members {



}

program : expression+ EOF;

PATH : '@'([0-9a-zA-Z_\/\;]+);
SIGNATURE: '('PATH*')'PATH;
object: PATH SIGNATURE;

primary : LITERAL #literal
        | IDENTIFIER #identifier
        | '(' expression ')' #expressionInBrackets
        | lambda #lambdaExpression
        | ifThenElse#ifThenElseExpression
        | 'new' object   #newClass
        |  '(' expression ')' ':' PATH '.' object #methodCall

        ;

expression : 'let' IDENTIFIER '=' lambda 'in'<assoc=right> expression #let
| logicExpression #throwLogicExpression
;

logicExpression: compareExpression operator=(AND | OR) expression #logic
| compareExpression #throwCompareExpression
;

compareExpression : sumExpression operator=(EQUAL | NOT_EQUAL | LESS_OR_EQUAL | MORE_OR_EQUAL | LESS | MORE) compareExpression #compare
| sumExpression #throwSumExpression
;


sumExpression : sumExpression operator=(PLUS | MINUS) multiplyExpression #addSub
| multiplyExpression  #throwMultiplyExpression
;

multiplyExpression : applications operator=(MULT | DIV | MOD) multiplyExpression #mulDiv
| applications #throwApplications ;

applications: applicationsList;
applicationsList returns [ApplicationTree application] @init {
        ArrayList<PrimaryExpressionContext > app=new ArrayList<PrimaryExpressionContext >();
        ApplicationTree tree = new ApplicationTree();

      }
      @after {
            if (app.size()==1) {
                 tree.unary=true;
                 tree.func=app.get(0);
                 //System.err.println("add");

            }    else {
            ApplicationTree root=tree;
            ApplicationTree cur=root;
            for (int i=app.size()-1;i>0;--i)  {
                 cur.func=app.get(i);
               //  System.err.println("add2");
                 ApplicationTree next=new ApplicationTree();
                 cur.next=next;
                 cur=next;  }

             cur.func=app.get(0);
             cur.unary=true;

             }
             $application=tree;

      }
       :  (primaryExpression { app.add($primaryExpression.ctx);})+;
primaryExpression: primary
	    ;

lambda : '\\' IDENTIFIER+ '->' expression  #lambdaWithArguments
       | '\\' '()' '->' expression  #lambdaWithoutArguments
       |   'new' object expression? #staticLambdaObject
       ;


ifThenElse: 'if' expression 'then' expression 'else' expression 'fi'
           ; 

functionArguments : (expression)* expression
                   ;




LINE_COMMENT : '//' .*? '\r'? '\n' -> skip ;
COMMENT : '/*' .*? '*/' -> skip ;

AND : '&&';
OR : '||';

EQUAL : '==';
NOT_EQUAL : '!=';
LESS_OR_EQUAL : '<=';
MORE_OR_EQUAL : '=>';
LESS : '<';
MORE : '>';

PLUS : '+';
MINUS : '-';

MULT : '*';
DIV : '/';
MOD : '%';


LITERAL : [0-9]+;
IDENTIFIER : [a-zA-Z][a-zA-Z0-9_]*;
WS : [ \t\r\n]+ -> skip;    